﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace PictrueSimpleSyndication
{
    public class NetPictureCacheInfo
    {
        /// <summary>
        /// 缓存状态
        /// </summary>
        public bool CacheStatus { get; set; }

        /// <summary>
        /// 网络地址
        /// </summary>
        public string UriPath { get; set; }

        /// <summary>
        /// 本地地址
        /// </summary>
        public string LocalPath { get; set; }

        /// <summary>
        /// 附带消息
        /// </summary>
        public string Message { get; set; }
    }

    public class NetPictureCache
    {
        private NetPictureCache()
        {
            if (!Directory.Exists("ImageCache"))
                Directory.CreateDirectory("ImageCache");
        }

        private static NetPictureCache _NetPictureCache = new NetPictureCache();

        public static NetPictureCache GetInstance() { return _NetPictureCache; }

        public const string IMAGE_CACHE_FINSH_EVENT = "ImageCacheFinsh";

        /// <summary>
        /// 网络路径的对应缓存状态
        /// </summary>
        private List<string> DownLoadList = new List<string>();

        /// <summary>
        /// 网络地址与本地地址的对照表
        /// </summary>
        private Dictionary<string, string> Url2LocalPath = new Dictionary<string, string>();

        /// <summary>
        /// 缓存图片
        /// </summary>
        /// <param name="picpath"></param>
        public async void CachePicture(string picpath)
        {
            string local_path = string.Empty;
            bool success = false;
            string message = string.Empty;
            bool ig = false;

            while (true)
            {
                // 存在历史则直接回调
                lock (Url2LocalPath)
                {
                    if (Url2LocalPath.ContainsKey(picpath))
                    {
                        success = true;
                        local_path = Url2LocalPath[picpath];
                        break;
                    }
                }

                // 正在下载的数据
                lock (DownLoadList)
                {
                    if (DownLoadList.Contains(picpath))
                    {
                        ig = true;
                        break;
                    }

                    DownLoadList.Add(picpath);
                }

                try
                {
                    WebClient client = new WebClient();

                    local_path = Path.Combine("ImageCache", Guid.NewGuid().ToString("N") + ".jpg");
                    await client.DownloadFileTaskAsync(picpath, local_path);

                    lock (Url2LocalPath)
                        Url2LocalPath[picpath] = local_path;

                    success = true;
                }
                catch (Exception ex)
                {
                    success = false;
                    message = ex.Message;
                }
                finally
                {
                    lock (DownLoadList)
                    {
                        DownLoadList.Remove(picpath);
                    }
                }

                break;
            }

            if (!ig)
            {
                EventTools.GetInstance().TriggerEvent(IMAGE_CACHE_FINSH_EVENT, new NetPictureCacheInfo
                {
                    Message = message,
                    CacheStatus = success,
                    LocalPath = local_path,
                    UriPath = picpath
                });
            }
        }

        private Dictionary<object, List<Action<object>>> Host2Func = new Dictionary<object, List<Action<object>>>();

        /// <summary>
        /// 观察某张图片的缓存
        /// </summary>
        /// <param name="func"></param>
        /// <param name="host"></param>
        public void WatchCachePicture(Action<object> func, object host)
        {
            EventTools.GetInstance().Register(IMAGE_CACHE_FINSH_EVENT, func);

            if (!Host2Func.ContainsKey(host)) lock (Host2Func) if (!Host2Func.ContainsKey(host))
                        Host2Func[host] = new List<Action<object>>();

            lock (Host2Func[host])
                Host2Func[host].Add(func);
        }

        /// <summary>
        /// 取消关注图片缓存
        /// </summary>
        /// <param name="picpath"></param>
        /// <param name="func"></param>
        public void UnWatchCachePicture(Action<object> func)
        {
            EventTools.GetInstance().UnRegister(IMAGE_CACHE_FINSH_EVENT, func);
        }

        /// <summary>
        /// 取消关注图片缓存
        /// </summary>
        /// <param name="host"></param>
        public void UnWatchCachePicture(object host)
        {
            if (!Host2Func.ContainsKey(host))
                return;

            lock (Host2Func[host])
            {
                foreach (var item in Host2Func[host])
                    EventTools.GetInstance().UnRegister(IMAGE_CACHE_FINSH_EVENT, item);

                Host2Func[host] = null;
            }
        }

        private const string CACHE_FILE_NAME = "NetPictureCache.adata";

        public void DeSerialize()
        {
            if (!File.Exists(CACHE_FILE_NAME))
                return;

            using (StreamReader sr = new StreamReader(CACHE_FILE_NAME))
            {
                while (sr.Peek() > 0)
                {
                    string line = sr.ReadLine();
                    var items = line.Split(new[] { '\t' }, StringSplitOptions.RemoveEmptyEntries);

                    if (items.Length != 2)
                        continue;

                    if (!File.Exists(items[1]))
                        continue;

                    Url2LocalPath[items[0]] = items[1];
                }
            }
        }

        public void Serialize()
        {
            if (!File.Exists(CACHE_FILE_NAME))
                File.Create(CACHE_FILE_NAME).Close();

            using (StreamWriter sw = new StreamWriter(CACHE_FILE_NAME))
            {
                foreach (var item in Url2LocalPath)
                {
                    sw.WriteLine(item.Key + "\t" + item.Value);
                }
            }
        }
    }
}
